package com.bckj.fastboot.flow.process;

import com.bckj.fastboot.core.lang.exception.BusinessException;
import com.bckj.fastboot.flow.model.*;
import lombok.extern.slf4j.Slf4j;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;

@Slf4j
public class BpmnProcessParser {


    public static BpmnProcess parseBpmn(String content) {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(new ByteArrayInputStream(content.getBytes()));

            // 解析 process 元素
            Element processElement = (Element) doc.getElementsByTagName("process").item(0);
            BpmnProcess process = new BpmnProcess(processElement.getAttribute("id"), processElement.getAttribute("name"));
            parseStartEvent(processElement, process);
            parseServiceTask(processElement, process);
            parseExclusiveGateway(processElement, process);
            parseParallelGateway(processElement, process);
            parseCallActivity(processElement, process);
            parseEndEvent(processElement, process);
            parseSequenceFlow(processElement, process);
            return process;
        } catch (Exception e) {
            log.error("解析BPMN失败" + e.getMessage(), e);
            throw new BusinessException("解析BPMN失败" + e.getMessage());
        }
    }

    private static void parseStartEvent(Element processElement, BpmnProcess process) {
        NodeList nodeList = processElement.getElementsByTagName("startEvent");
        for (int i = 0; i < nodeList.getLength(); i++) {
            Element element = (Element) nodeList.item(i);
            BpmnStartEvent startEvent = new BpmnStartEvent(element.getAttribute("id"), element.getAttribute("name"), element.getNodeName());
            process.getStartEvents().add(startEvent);
            process.addElement(startEvent);
        }
    }

    private static void parseServiceTask(Element processElement, BpmnProcess process) {
        NodeList nodeList = processElement.getElementsByTagName("serviceTask");
        for (int i = 0; i < nodeList.getLength(); i++) {
            Element element = (Element) nodeList.item(i);
            BpmnServiceTask serviceTask = new BpmnServiceTask(element.getAttribute("id"), element.getAttribute("name"), element.getNodeName());
            serviceTask.setExpression(element.getAttribute("flowable:expression"));
            serviceTask.setDelegateExpression(element.getAttribute("flowable:delegateExpression"));
            serviceTask.setDelegateClass(element.getAttribute("flowable:class"));
            process.addServiceTask(serviceTask);
            process.addElement(serviceTask);
        }
    }

    private static void parseExclusiveGateway(Element processElement, BpmnProcess process) {
        NodeList nodeList = processElement.getElementsByTagName("exclusiveGateway");
        for (int i = 0; i < nodeList.getLength(); i++) {
            Element element = (Element) nodeList.item(i);
            BpmnExclusiveGateway exclusiveGateway = new BpmnExclusiveGateway(element.getAttribute("id"), element.getAttribute("name"), element.getNodeName());
           process.addElement(exclusiveGateway);
        }
    }

    private static void parseParallelGateway(Element processElement, BpmnProcess process) {
        NodeList nodeList = processElement.getElementsByTagName("parallelGateway");
        for (int i = 0; i < nodeList.getLength(); i++) {
            Element element = (Element) nodeList.item(i);
            BpmnParallelGateway parallelGateway = new BpmnParallelGateway(element.getAttribute("id"), element.getAttribute("name"), element.getNodeName());
            process.addElement(parallelGateway);
        }
    }

    private static void parseEndEvent(Element processElement, BpmnProcess process) {
        NodeList nodeList = processElement.getElementsByTagName("endEvent");
        for (int i = 0; i < nodeList.getLength(); i++) {
            Element element = (Element) nodeList.item(i);
            BpmnEndEvent endEvent = new BpmnEndEvent(element.getAttribute("id"), element.getAttribute("name"), element.getNodeName());
            process.addElement(endEvent);
        }
    }

    private static void parseSequenceFlow(Element processElement, BpmnProcess process) {
        NodeList nodeList = processElement.getElementsByTagName("sequenceFlow");
        for (int i = 0; i < nodeList.getLength(); i++) {
            Element element = (Element) nodeList.item(i);
            String sourceRef = element.getAttribute("sourceRef");
            String targetRef = element.getAttribute("targetRef");
            BpmnSequenceFlow sequenceFlow = new BpmnSequenceFlow(element.getAttribute("id"), element.getAttribute("name"), element.getNodeName(), sourceRef, targetRef);
            //获取表达式
            String expression = null;
            NodeList condList = element.getElementsByTagName("conditionExpression");
            if (condList.getLength()> 0) {
                Element condElement = (Element) condList.item(0);
                expression = condElement.getTextContent();
            }
            sequenceFlow.setExpression(expression);
            process.addElement(sequenceFlow);
            // 存储 sequenceFlow 信息
            BpmnElement sourceElement = process.getElementById(sourceRef);
            if (sourceElement!= null) {
                sourceElement.addOutgoingFlow(sequenceFlow);
            }
            BpmnElement targetElement = process.getElementById(targetRef);
            if (targetElement!= null) {
                targetElement.addIncomingFlow(sequenceFlow);
            }
        }
    }

    private static void parseCallActivity(Element processElement, BpmnProcess process) {
        NodeList nodeList = processElement.getElementsByTagName("callActivity");
        for (int i = 0; i < nodeList.getLength(); i++) {
            Element element = (Element) nodeList.item(i);
            BpmnCallActivity callActivity = new BpmnCallActivity(element.getAttribute("id"), element.getAttribute("name"), element.getNodeName(), element.getAttribute("calledElement"));
            // 获取extensionElements
            NodeList extensionList = element.getElementsByTagName("extensionElements");
            if (extensionList.getLength() > 0) {
                Element extensionElement = (Element) extensionList.item(0);
                setInOutMap(extensionElement, callActivity, "flowable:in");
                setInOutMap(extensionElement, callActivity, "flowable:out");
            }
            process.addElement(callActivity);
        }
    }

    private static void setInOutMap(Element extensionElement, BpmnCallActivity callActivity, String tagName) {
        NodeList outList = extensionElement.getElementsByTagName(tagName);
        List<BpmnIOParam> params = new ArrayList<>();
        for (int j = 0; j < outList.getLength(); j++) {
            Element e = (Element) outList.item(j);
            BpmnIOParam param = new BpmnIOParam();
            param.setSource(e.getAttribute("source"));
            param.setTarget(e.getAttribute("target"));
            param.setSourceExpression(e.getAttribute("sourceExpression"));
            param.setTargetExpression(e.getAttribute("targetExpression"));
            params.add(param);
        }
        if ("flowable:in".equals(tagName)) {
            callActivity.setInParams(params);
        } else {
            callActivity.setOutParams(params);
        }
    }
}
